package com.neusoft.neuiotms.dcom.service.report.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.neusoft.bizcore.web.constant.ExceptionConstants;
import com.neusoft.bizcore.web.exception.UnitedException;
import com.neusoft.bizcore.web.support.Searchable;
import com.neusoft.neuiotms.dcom.converter.report.ReportToolConverter;
import com.neusoft.neuiotms.dcom.dto.report.ReportToolDTO;
import com.neusoft.neuiotms.dcom.model.report.ReportTool;
import com.neusoft.neuiotms.dcom.model.report.ReportToolItem;
import com.neusoft.neuiotms.dcom.repository.report.ReportToolRepository;
import com.neusoft.neuiotms.dcom.service.report.ReportToolService;

@Service
public class ReportToolServiceImpl implements ReportToolService {

    @Autowired
    private ReportToolRepository reportToolRepository;
    @Autowired
    private ReportToolConverter reportToolConverter;
    @PersistenceContext
    private EntityManager entityManager; // 实体管理

    @Override
    public List<ReportToolDTO> index(final Searchable searchable) {
        final List<ReportTool> results = this.reportToolRepository.index(searchable);
        return this.reportToolConverter.toListDTO(results);
    }

    @Override
    public Page<ReportToolDTO> search(final Pageable pageable, final Searchable searchable) {
        final Page<ReportTool> results = this.reportToolRepository.search(pageable, searchable);
        return this.reportToolConverter.toPageDTO(results);
    }

    @Override
    public ReportToolDTO create(final ReportToolDTO dto) {

        ReportTool model = this.reportToolConverter.toModel(dto);
        model = this.reportToolRepository.save(model);
        return this.reportToolConverter.toDTO(model);
    }

    @Override
    public ReportToolDTO update(final Long id, final ReportToolDTO dto) {

        ReportTool model;
        try {
            model = this.reportToolRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        dto.setId(model.getId());
        model = this.reportToolConverter.toModel(dto);
        model = this.reportToolRepository.save(model);
        return this.reportToolConverter.toDTO(model);
    }

    @Override
    public ReportToolDTO details(final Long id) {

        ReportTool model;
        try {
            model = this.reportToolRepository.getOne(id);
        } catch (final EntityNotFoundException e) {
            throw new UnitedException(ExceptionConstants.ENTITY_NOT_FOUND, new Object[] {id });
        }

        return this.reportToolConverter.toDTO(model);
    }

    @Override
    public void delete(final Long id) {
        this.reportToolRepository.deleteById(id);
    }

    @SuppressWarnings("unchecked")
    @Override
    public Page<Map<String, Object>> reportData(final Pageable pageable, final Searchable searchable) {

        final Pageable pageable2 = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize());

        final Map<String, Query> queryMap = this.generateQuery(searchable, pageable);
        final Query query = queryMap.get("baseQuery");
        final Query queryCount = queryMap.get("countQuery");
        final Page<Map<String, Object>> resultPage = new PageImpl<Map<String, Object>>(query.getResultList(), pageable2,
                Integer.valueOf(queryCount.getSingleResult().toString()));

        return resultPage;
    }

    private Map<String, Query> generateQuery(final Searchable searchable, final Pageable... pageable) {

        ReportTool toolModel;
        Query query;

        final Map<String, Query> queryMap = new HashMap<>();

        // 是否分页
        final Boolean isPage = pageable.length > 0 ? true : false;

        final StringBuffer sqlSelect = new StringBuffer();
        final StringBuffer sqlCondition = new StringBuffer();

        // 获取查询sql
        if (!StringUtils.isEmpty(searchable.getStrValue("code"))) {
            toolModel = this.reportToolRepository.findByCode(searchable.getStrValue("code"));
            sqlSelect.append(toolModel.getSelectSql());
        } else {
            throw new UnitedException("配置报表编码为空！");
        }
        // 获取整体sql，返回列表数据集
        final List<ReportToolItem> itemModel = toolModel.getReportToolItem();

        if ((null != itemModel) && (itemModel.size() > 0)) {
            itemModel.forEach(e -> {
                // 判断是否为String类型
                if (e.getSqlCodeType().equals("String")) {
                    if (!StringUtils.isEmpty(searchable.getStrValue(e.getSqlCode()))) {
                        sqlCondition.append(e.getSqlCondition());
                    }
                }
                // 判断是否为时间类型
                if (e.getSqlCodeType().equals("Long")) {
                    if (!StringUtils.isEmpty(searchable.getLongValue(e.getSqlCode()))) {
                        sqlCondition.append(e.getSqlCondition());
                    }
                }
            });

            query = this.entityManager.createNativeQuery(sqlSelect.append(sqlCondition.toString()).toString());
            query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);

            //传参
            itemModel.forEach(e -> {
                // 判断是否为String类型
                if (e.getSqlCodeType().equals("String")) {
                    if (!StringUtils.isEmpty(searchable.getStrValue(e.getSqlCode()))) {
                        query.setParameter(e.getSqlCode(), searchable.getStrValue(e.getSqlCode()));
                    }
                }
                // 判断是否为时间类型
                if (e.getSqlCodeType().equals("Long")) {
                    if (!StringUtils.isEmpty(searchable.getLongValue(e.getSqlCode()))) {
                        query.setParameter(e.getSqlCode(), searchable.getLongValue(e.getSqlCode()) / 1000);
                    }
                }
            });
            queryMap.put("baseQuery", query);
        } else {
            // 无查询条件
            query = this.entityManager.createNativeQuery(sqlSelect.toString());
            query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
            queryMap.put("baseQuery", query);
        }

        //分页
        if (isPage) {
            query.setFirstResult((pageable[0].getPageNumber()) * pageable[0].getPageSize());
            query.setMaxResults(pageable[0].getPageSize());

            final StringBuffer sqlCount = new StringBuffer();
            sqlCount.append("SELECT COUNT(1) ");

            final Query queryCount =
                    this.entityManager.createNativeQuery(sqlCount.append(sqlCondition.toString()).toString());

            // 传参
            if ((null != itemModel) && (itemModel.size() > 0)) {
                itemModel.forEach(e -> {
                    // 判断是否为String类型
                    if (e.getSqlCodeType().equals("String")) {
                        if (!StringUtils.isEmpty(searchable.getStrValue(e.getSqlCode()))) {
                            queryCount.setParameter(e.getSqlCode(), searchable.getStrValue(e.getSqlCode()));
                        }
                    }
                    // 判断是否为时间类型
                    if (e.getSqlCodeType().equals("Long")) {
                        if (!StringUtils.isEmpty(searchable.getLongValue(e.getSqlCode()))) {
                            queryCount.setParameter(e.getSqlCode(), searchable.getLongValue(e.getSqlCode()) / 1000);
                        }
                    }
                });
            }
            queryMap.put("countQuery", queryCount);
        } else {
            throw new UnitedException("pageable无数据!");
        }

        return queryMap;
    }

}
